<?php
//后根序遍历目录, 读取所有子目录和文件
//张志斌 954861399@qq.com
class Dir
{
	public $dir = '';
	public $fileList = [];
	public $uniqueList = []; //存放每一级目录或文件的md5编码等信息
	public $fileChainList = []; //存放每个文件对应的目录链, 比如: a/b/c/file.md : [md5(file.md) => [md5(a), md5(b), md5(c)]]
    public $fileHashList = [];//存放hash与文件路径的映射关系

	public function __construct($dir)
	{
        $stack = array();
        $dir = str_replace('\\', '/', realpath($dir));
        $current = [
            'dir' => $dir.'/',
            'files' => $this->getDirFiles($dir)
        ];

        while(!empty($stack) || !empty($current['files'])){
            $isStack = 0;
            foreach ($current['files'] as $k => $fileName) {
                $pathName = $current['dir'].$fileName;
                if (is_file($pathName)) {
                    $this->fileList[] = $pathName;
                } elseif (is_dir($pathName) && count($this->getDirFiles($pathName)) > 0) {
                    //当前是目录, 就把其以后的数据压栈
                    $tmp['files'] = array_slice($current['files'], $k+1);
                    $tmp['dir'] = $current['dir'];
                    array_push($stack, $tmp);
                    $isStack = 1;

                    $current['files'] = $this->getDirFiles($pathName); //读当前目录中的文件
                    $current['dir'] = $pathName.'/';

                    break;
                }
            }

            if (empty($isStack)) { //没有入栈, 说明当前目录里全是文件
                $current = array_pop($stack); //读取栈里保存的目录/文件
            }
        }

        foreach ($this->fileList as $k => $v) {
            $this->fileList[$k] = str_replace('\\', '/', $v);
        }
	}

	//读取所有子目录和文件, $dir目录最后不要有'/'
	public static function ini($dir)
	{
		return new self($dir);
	}

    public function getDirFiles($dir)
    {
        $list = [];
        $files = scandir($dir);
        foreach ($files as $fileName) {
            if (substr($fileName, 0, 1) != '.') {
                $list[] = $fileName;
            }
        }

        return $list;
    }

	//替换路径里的字符串
	public function replace($target, $replace='')
	{
		foreach ($this->fileList as $k => $v) {
			$this->fileList[$k] = str_replace($target, $replace, $v);
		}
		return $this;
	}
    
    //替换路径里的字符串
    public function pregReplace($pattern, $replace='')
    {
        foreach ($this->fileList as $k => $v) {
            $this->fileList[$k] = preg_replace("#$pattern#i", $replace, $v);
        }
        return $this;
    }
	
	//筛选获取指定的后缀
	public function extension($ext)
	{
		foreach ($this->fileList as $k => $v) {
			$extension = pathinfo($v, PATHINFO_EXTENSION);
			if (strtolower($extension) != $ext) {
				unset($this->fileList[$k]);
			}
		}
		return $this;
	}
	
	public function sortFiles($isReverse = FALSE)
    {
        if ($isReverse) {
            rsort($this->fileList);
        } else {
            sort($this->fileList);
        }
        
        return $this;
    }

    //生成hash值, 避免中文乱码, 以及减少长度
    public function getHash($str, $len=16)
    {
        return substr(md5($str), 0, $len);
    }

    //给每层目录/文件创建唯一值
    public function createUniqueCode()
    {
        $list = [];
        foreach ($this->fileList as $filePath) {
            $arr = explode('/', $filePath);
            $count = count($arr);

            $hashChain = [];
            for ($i=0; $i < $count; $i++) {
                $path = implode('/', array_slice($arr, 0, $i+1, true));
                $hash = $this->getHash($path);
                $hashChain[] = $hash;
                if (empty($list[$hash])) {
                    $parent = (strpos($path, '/') === false)
                        ? ''
                        : $this->getHash(implode('/', array_slice($arr, 0, $i, true)));

                    if ($i == $count -1) {
                        $isFile = '1';
                        array_pop($hashChain);
                        $this->fileChainList[$hash] = $hashChain;
                        $this->fileHashList[$filePath] = $hash;
                    } else {
                        $isFile = '0';
                    }
                    $list[$hash] = ['path' => $path, 'current' => $arr[$i], 'parent' => $parent, 'is_file' => $isFile];
                }
            }
        }
        $this->uniqueList = $list;
        return $this;
    }

}